#include <SDL/SDL.h>
#include <SDL/SDL_ttf.h>
#include <SDL/SDL_image.h>
#include <glib.h>

#include <vector>
#include <string>

#include "screen.h"
#include "errors.h"
#include "global.h"
#include "img.h"

static SDL_Surface* screen = NULL;

static bool i_sdl = FALSE;
static bool i_ttf = FALSE;

static Image* background = NULL;
static vector<Renderable*> render_items;

#define CASE_STR(x,y) case x: str = y; break
static const char* glErrorString(GLenum err) {
	string str;
	switch (err) {
		CASE_STR(GL_NO_ERROR, "No error");
		CASE_STR(GL_INVALID_ENUM, "Invalid enum");
		CASE_STR(GL_INVALID_VALUE, "Invalid value");
		CASE_STR(GL_INVALID_OPERATION, "Invalid operation");
		CASE_STR(GL_STACK_OVERFLOW, "Stack overflow");
		CASE_STR(GL_STACK_UNDERFLOW, "Stack underflow");
		CASE_STR(GL_OUT_OF_MEMORY, "Out of memory");
		default: str = "Unknown error " + err; break;
	}
	return str.c_str();
}

#ifdef EGL /* Annoying EGL crap */
static const char* eglErrorString(EGLint err) {
	string str;
	switch (err) {
		CASE_STR(EGL_SUCCESS, "No error");
		CASE_STR(EGL_NOT_INITIALIZED, "EGL not, or could not be, initialised");
		CASE_STR(EGL_BAD_ACCESS, "Access violation");
		CASE_STR(EGL_BAD_ALLOC, "Could not allocate resources");
		CASE_STR(EGL_BAD_ATTRIBUTE, "Invalid attribute");
		CASE_STR(EGL_BAD_CONTEXT, "Invalid context specified");
		CASE_STR(EGL_BAD_CONFIG, "Invald frame buffer configuration specified");
		CASE_STR(EGL_BAD_CURRENT_SURFACE, "Current window, pbuffer or pixmap surface is no longer valid");
		CASE_STR(EGL_BAD_DISPLAY, "Invalid display specified");
		CASE_STR(EGL_BAD_SURFACE, "Invalid surface specified");
		CASE_STR(EGL_BAD_MATCH, "Bad argument match");
		CASE_STR(EGL_BAD_PARAMETER, "Invalid paramater");
		CASE_STR(EGL_BAD_NATIVE_PIXMAP, "Invalid NativePixmap");
		CASE_STR(EGL_BAD_NATIVE_WINDOW, "Invalid NativeWindow");
		CASE_STR(EGL_CONTEXT_LOST, "APM event caused context loss");
		default: str = "Unknown error " + err; break;
	}
	return str.c_str();
}

static const EGLint egl_config_attr[] = {
#ifdef PANDORA
  // No idea if this is right. Ken's Labyrinth source says so.
	EGL_RED_SIZE,              5,
	EGL_GREEN_SIZE,            6,
	EGL_BLUE_SIZE,             5,
	EGL_ALPHA_SIZE,            0,
	EGL_DEPTH_SIZE,            16,
	EGL_RENDERABLE_TYPE,       EGL_OPENGL_ES_BIT,
	//EGL_BIND_TO_TEXTURE_RGBA,  EGL_TRUE,
#elif defined(GP2X)
	EGL_BUFFER_SIZE,           0,
	EGL_DEPTH_SIZE,            16,
#endif
	EGL_SURFACE_TYPE,          EGL_WINDOW_BIT,
	EGL_NONE
};

eglConnection e_con = {
	EGL_NO_DISPLAY,
	EGL_NO_CONTEXT,
	EGL_NO_SURFACE,
	(NativeWindowType)0
};

int init_egl() {
  // TODO: Use SDL_syswm to set up EGL context for non GP2X/Caanoo targets
	EGLConfig e_config;
	EGLint nc;

	if ((e_con.display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY) {
		ELOG("EGL error: %s", eglErrorString(eglGetError()));
		return ESCREEN_INIT;
	}
	if (eglInitialize(e_con.display, NULL, NULL) == EGL_FALSE) {
		ELOG("EGL error: %s", eglErrorString(eglGetError()));
		return ESCREEN_INIT;
	}
	if (!eglChooseConfig(e_con.display, egl_config_attr, &e_config, 1, &nc)) {
		ELOG("EGL error: %s", eglErrorString(eglGetError()));
		return ESCREEN_INIT;
	}
	if ((e_con.context = eglCreateContext(e_con.display, e_config, EGL_NO_CONTEXT, NULL)) == EGL_NO_CONTEXT) {
		ELOG("EGL error: %s", eglErrorString(eglGetError()));
		return ESCREEN_INIT;
	}

	if (!(e_con.window = (NativeWindowType)(malloc(16*1024)))) {
		ELOG("%s", "Malloc error");
		return ESCREEN_INIT;
	}
	if ((e_con.surface = eglCreateWindowSurface(e_con.display, e_config, e_con.window, NULL)) == EGL_NO_SURFACE) {
		ELOG("EGL error: %s", eglErrorString(eglGetError()));
		return ESCREEN_INIT;
	}
	if (!eglMakeCurrent(e_con.display, e_con.surface, e_con.surface, e_con.context)) {
		ELOG("EGL error: %s", eglErrorString(eglGetError()));
		return ESCREEN_INIT;
	}
	return 0;
}

#endif /* Annoying EGL crap */

int init_screen() {
#ifdef EGL
	int flags;
#else
	SDL_Surface* wm_icon = NULL;
#endif

	/* Initialize SDL */
	SDL_ClearError();
	if (SDL_Init (SDL_INIT_FLAGS) < 0) {
		ELOG("SDL initialisation error: %s", SDL_GetError());
		return ESCREEN_INIT;
	}
	i_sdl = TRUE;
	SDL_ClearError();

#ifndef EGL
	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
	SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
	SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 6 );
	SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
	SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 0 );
	SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 0 );

	if ((wm_icon = IMG_Load(WM_ICON))) {
		SDL_WM_SetIcon(wm_icon, NULL);
	} else {
		ELOG("warning: Could not load %s", WM_ICON);
	}
#endif
	SDL_ShowCursor(SDL_DISABLE);

	if (!(screen = SDL_SetVideoMode (320, 240, 16, VIDEO_FLAGS))) {
		ELOG("SDL Vidmode error: %s", SDL_GetError());
		return ESCREEN_INIT;
	}
#ifndef EGL
	SDL_WM_SetCaption("CanTuna", "CanTuna");
#endif
	SDL_ClearError();

#ifdef EGL
	if ((flags = init_egl())) {
		return flags;
	}
#endif

	if (TTF_Init() == -1) {
		ELOG("SDL_ttf intialisation error: %s", SDL_GetError());
		return ESDL_INIT_TTF;
	}
	i_ttf = TRUE;
	SDL_ClearError();

	/* Let's do some OpenGL */
	glViewport(0, 0, 320, 240);
	glDisable(GL_CULL_FACE);
	glDisable(GL_LIGHTING);
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_TEXTURE_2D);
	glEnable(GL_SCISSOR_TEST);
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);

	glClearColor(0, 0.4, 0, 0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(-WAX, WAX, -HAX, HAX, -1.0, 1.0);
	return 0;
}

void glCheckError(const char* file, unsigned line) {
	GLenum err;
	while ((err = glGetError())) {
		log_print(file, line, "GL error: %s", glErrorString(err));
	}
}

gboolean idle_screen(gpointer data) {
	vector<Renderable*>::iterator it;
	glScissor(0, 0, WAX * 2, HAX * 2);
	glClear(GL_COLOR_BUFFER_BIT);
	if (background) {
		background->render();
	}
	for (it = render_items.begin(); it != render_items.end(); it ++) {
		(*it)->render();
		GL_CHECK_ERROR();
	}
	glFlush();
#ifdef EGL
	eglSwapBuffers(e_con.display, e_con.surface);
#else
	SDL_GL_SwapBuffers();
#endif
	// 40ms = 25 FPS
	SDL_Delay(40);
	return TRUE;
}

#if 0
static const char* glTexFormatString(GLenum fmt) {
	string str;
	switch (fmt) {
		CASE_STR(GL_ALPHA, "Alpha");
		CASE_STR(GL_RGB, "RGB");
		CASE_STR(GL_RGBA, "RGBA");
		CASE_STR(GL_LUMINANCE, "Luminance");
		CASE_STR(GL_LUMINANCE_ALPHA, "Luminance with alpha");
		default: {
			str = "Format " + (unsigned)fmt;
			str += " not supported by OpenGL|ES 1.1";
			break;
		}
	}
	return str.c_str();
}
#endif

bool set_bg(const char* fname) {
	bool ret;
	if (!background) {
		ELOG("%s", "New background");
		background = new Image;
	}
	if ((ret = background->fromFile(fname))) {
		return set_bg(background);
	}
	return FALSE;
}

bool set_bg(Image* img) {
	if (img && img->scale(WAX * 2, HAX * 2)) {
		background = img;
	} else {
		return FALSE;
	}
	return TRUE;
}

void kill_screen() {
	//set_bg(NULL);
#ifdef EGL
	if (e_con.display != EGL_NO_DISPLAY) {
		eglMakeCurrent(e_con.display, NULL, NULL, EGL_NO_CONTEXT);
		if (e_con.context != EGL_NO_CONTEXT) {
			eglDestroyContext(e_con.display, e_con.context);
		}
		if (e_con.surface != EGL_NO_SURFACE) {
			eglDestroySurface(e_con.display, e_con.surface);
		}
		eglTerminate(e_con.display);
	}
	if (e_con.window) {
		free((void*)e_con.window);
	}
#endif
	if (i_ttf) {
		TTF_Quit();
	}
	if (i_sdl) {
		SDL_Quit();
	}
}
/* vim:set ts=2 sts=2 sw=2 noexpandtab: */
